🇨🇳 中文(面试标准答案)
🇺🇸 English (interview version)
🇨🇳 中文口语版
👉 举例:
🇺🇸 English
xfunction debounce(fn, delay = 300) { let timer = null; return function (args) { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); }, delay); };}🇨🇳:
核心就是用 setTimeout + clearTimeout,每次触发都清掉上一次的定时器,只保留最后一次。
🇺🇸:
We use setTimeout and cancel previous timers with clearTimeout to ensure only the last call is executed.
xxxxxxxxxxfunction throttle(fn, delay = 300) { let lastTime = 0; return function (args) { const now = Date.now(); if (now - lastTime >= delay) { lastTime = now; fn.apply(this, args); } };}xxxxxxxxxxfunction throttle(fn, delay = 300) { let timer = null; return function (args) { if (timer) return; timer = setTimeout(() => { fn.apply(this, args); timer = null; }, delay); };}🇨🇳: 节流就是“规定时间内只允许执行一次”,要么用时间戳控制,要么用定时器锁住。
🇺🇸: Throttle ensures the function runs only once per time interval using either timestamps or a timer lock.
👉 可以(leading debounce)
x
function debounce(fn, delay, immediate = false) { let timer = null; return function (args) { const callNow = immediate && !timer; clearTimeout(timer); timer = setTimeout(() => { timer = null; }, delay); if (callNow) fn.apply(this, args); };}
👉 可以(trailing throttle),但需要稍微升级实现(面试加分点)
| 场景 | 技术 |
|---|---|
| 输入搜索 | 防抖 |
| resize | 节流 |
| scroll监听 | 节流 |
| 按钮点击防重复提交 | 防抖 |
| 游戏按键 | 节流 |
🇨🇳 中文
🇺🇸 English
可以这样说:
防抖适合“最终状态”,节流适合“过程控制”
setTimeout 延迟更新xxxxxxxxxximport { useEffect, useState } from "react";export function useDebounce(value, delay = 300) { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const timer = setTimeout(() => { setDebouncedValue(value); }, delay); return () => { clearTimeout(timer); }; }, [value, delay]); return debouncedValue;}🇨🇳: 每次 value 变化时我不会立即更新,而是启动一个定时器。如果在 delay 时间内 value 又变化了,就清除上一次的定时器,只保留最后一次更新。
🇺🇸: It delays updating the state until the value stops changing for a given delay.
xxxxxxxxxxconst [input, setInput] = useState("");const debouncedInput = useDebounce(input, 500);useEffect(() => { // 只在“停下来之后”触发 fetch(`/api/search?q=${debouncedInput}`);}, [debouncedInput]);
lastTime 或 timer 控制xxxxxxxxxximport { useEffect, useRef, useState } from "react";export function useThrottle(value, delay = 300) { const [throttledValue, setThrottledValue] = useState(value); const lastTime = useRef(0); useEffect(() => { const now = Date.now(); if (now - lastTime.current >= delay) { lastTime.current = now; setThrottledValue(value); } }, [value, delay]); return throttledValue;}xxxxxxxxxximport { useEffect, useRef, useState } from "react";export function useThrottle(value, delay = 300) { const [throttledValue, setThrottledValue] = useState(value); const timer = useRef(null); useEffect(() => { if (timer.current) return; timer.current = setTimeout(() => { setThrottledValue(value); timer.current = null; }, delay); }, [value, delay]); return throttledValue;}🇨🇳: 节流就是控制更新频率,在一定时间内只允许执行一次 state 更新。
🇺🇸: Throttle limits how often state can be updated during continuous changes.
面试官经常追问👇
👉 答:
🇨🇳: 因为 useRef 不会触发重新渲染,而且可以跨 render 保持同一个值。
🇺🇸: useRef keeps a stable mutable value across renders without causing re-renders.
| 维度 | useDebounce | useThrottle |
|---|---|---|
| 触发方式 | 停止后执行 | 按间隔执行 |
| 场景 | 搜索 | 滚动 |
| 控制方式 | 清定时器 | 限制频率 |
👉 可以升级(高级面试题):
x
export function useDebouncedCallback(fn, delay = 300) { const timer = useRef(null); return (args) => { clearTimeout(timer.current); timer.current = setTimeout(() => { fn(args); }, delay); };}
🇨🇳 中文
🇺🇸 English